package cn.heyige.backend.algorithm;

import cn.heyige.backend.entity.UserDO;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;

import java.time.LocalDateTime;

import static cn.heyige.backend.algorithm.DataConstant.*;

/**
 * 梅花易数核心算法 - 扰动增强版
 *
 * 遵循传统梅花易数起卦方法，同时增加用户输入数字和心情的扰动因素
 * 以提升预测准确率，保证算法的专业性和准确性
 *
 * @author CodeBuddy
 */
@Slf4j
public class MeihuaCalculator {

    /**
     * 传统算法入口 - 梅花易数预测
     *
     * @param user 用户信息（可为null）
     * @return 预测结果
     */
    public static PredictionResult predict(UserDO user) {
        try {
            // 1. 获取真太阳时
            LocalDateTime dateTime = getSolarTime(user);
            
            // 2. 转换农历
            LunarDate lunarDate = LunarCalculator.getPlumNumber(dateTime);
            
            // 3. 计算卦数（传统算法）
            GuaNumbers guaNumbers = calculateGuaNumbers(lunarDate);
            
            // 4. 生成卦象
            GuaInfo guaInfo = generateGua(guaNumbers);
            
            // 5. 分析五行关系
            WuxingAnalysis wuxingAnalysis = analyzeWuxing(guaInfo);
            
            // 6. 计算准确性
            double accuracy = calculateAccuracy(wuxingAnalysis, user, lunarDate);
            
            // 7. 构建结果
            return PredictionResult.builder()
                    .originalGua(guaInfo.originalGua)
                    .changedGua(guaInfo.changedGua)
                    .mutualGua(guaInfo.mutualGua)
                    .bodyGua(guaInfo.bodyGua)
                    .useGua(guaInfo.useGua)
                    .movingYao(guaNumbers.movingYao)
                    .wuxingRelation(wuxingAnalysis.relation)
                    .accuracy(accuracy)
                    .timestamp(System.currentTimeMillis())
                    .build();
                    
        } catch (Exception e) {
            log.error("梅花易数计算失败", e);
            throw new RuntimeException("预测计算失败", e);
        }
    }

    /**
     * 扰动增强算法入口 - 梅花易数预测
     *
     * @param user 用户信息（可为null）
     * @param userNumber 用户输入的随机数
     * @param mood 用户心情状态 (1-5, 3为平静)
     * @return 预测结果
     */
    public static PredictionResult predictWithDisturbance(UserDO user, Integer userNumber, Integer mood) {
        try {
            // 1. 获取真太阳时
            LocalDateTime dateTime = getSolarTime(user);
            
            // 2. 转换农历
            LunarDate lunarDate = LunarCalculator.getPlumNumber(dateTime);
            
            // 3. 计算卦数（传统算法 + 扰动因素）
            GuaNumbers guaNumbers = calculateGuaNumbersWithDisturbance(lunarDate, userNumber, mood);
            
            // 4. 生成卦象
            GuaInfo guaInfo = generateGua(guaNumbers);
            
            // 5. 分析五行关系
            WuxingAnalysis wuxingAnalysis = analyzeWuxing(guaInfo);
            
            // 6. 计算准确性（扰动增强版）
            double accuracy = calculateAccuracyWithDisturbance(wuxingAnalysis, user, lunarDate, userNumber, mood);
            
            // 7. 构建结果
            return PredictionResult.builder()
                    .originalGua(guaInfo.originalGua)
                    .changedGua(guaInfo.changedGua)
                    .mutualGua(guaInfo.mutualGua)
                    .bodyGua(guaInfo.bodyGua)
                    .useGua(guaInfo.useGua)
                    .movingYao(guaNumbers.movingYao)
                    .wuxingRelation(wuxingAnalysis.relation)
                    .accuracy(accuracy)
                    .timestamp(System.currentTimeMillis())
                    .build();
                    
        } catch (Exception e) {
            log.error("梅花易数计算失败", e);
            throw new RuntimeException("预测计算失败", e);
        }
    }

    /**
     * 获取真太阳时
     */
    private static LocalDateTime getSolarTime(UserDO user) {
        LocalDateTime dateTime = LocalDateTime.now();
        if (user != null && StrUtil.isNotBlank(user.getBirthLocation())) {
            return SolarTimeCalculator.adjustTimeByLocation(dateTime, user.getBirthLocation());
        }
        return dateTime;
    }

    /**
     * 计算卦数（传统算法）
     */
    private static GuaNumbers calculateGuaNumbers(LunarDate lunarDate) {
        // 天干地支数值计算（传统算法）
        int heavenlyStem = lunarDate.getLunarHour().getLunarDay().getLunarMonth()
                .getLunarYear().getSixtyCycle().getHeavenStem().getIndex() + 1;
        int earthlyBranch = lunarDate.getLunarHour().getLunarDay().getLunarMonth()
                .getLunarYear().getSixtyCycle().getEarthBranch().getIndex() + 1;
        int yearValue = heavenlyStem + earthlyBranch;
        
        int upperNum = (yearValue + lunarDate.getMonth() + lunarDate.getDay()) % 8;
        int lowerNum = (yearValue + lunarDate.getMonth() + lunarDate.getDay() + lunarDate.getHour()) % 8;
        int movingYao = (yearValue + lunarDate.getMonth() + lunarDate.getDay() + lunarDate.getHour()) % 6;
        
        // 处理0值
        if (upperNum == 0) upperNum = 8;
        if (lowerNum == 0) lowerNum = 8;
        if (movingYao == 0) movingYao = 6;
        
        return new GuaNumbers(upperNum, lowerNum, movingYao);
    }

    /**
     * 计算卦数（扰动增强算法）
     */
    private static GuaNumbers calculateGuaNumbersWithDisturbance(LunarDate lunarDate, Integer userNumber, Integer mood) {
        // 天干地支数值计算（传统算法）
        int heavenlyStem = lunarDate.getLunarHour().getLunarDay().getLunarMonth()
                .getLunarYear().getSixtyCycle().getHeavenStem().getIndex() + 1;
        int earthlyBranch = lunarDate.getLunarHour().getLunarDay().getLunarMonth()
                .getLunarYear().getSixtyCycle().getEarthBranch().getIndex() + 1;
        int yearValue = heavenlyStem + earthlyBranch;
        
        // 基础计算
        int baseUpper = yearValue + lunarDate.getMonth() + lunarDate.getDay();
        int baseLower = baseUpper + lunarDate.getHour();
        int baseMoving = baseLower;
        
        // 添加用户输入数字扰动
        int userNumberDisturbance = (userNumber != null) ? userNumber : 0;
        
        // 添加心情扰动（心情值越偏离3，扰动越大）
        int moodDisturbance = 0;
        if (mood != null) {
            moodDisturbance = Math.abs(mood - 3) * 2; // 心情偏离3的程度乘以2作为扰动
        }
        
        // 计算扰动后的数值
        int upperNum = (baseUpper + userNumberDisturbance + moodDisturbance) % 8;
        int lowerNum = (baseLower + userNumberDisturbance + moodDisturbance) % 8;
        int movingYao = (baseMoving + userNumberDisturbance + moodDisturbance) % 6;
        
        // 处理0值
        if (upperNum == 0) upperNum = 8;
        if (lowerNum == 0) lowerNum = 8;
        if (movingYao == 0) movingYao = 6;
        
        return new GuaNumbers(upperNum, lowerNum, movingYao);
    }

    /**
     * 生成卦象
     */
    private static GuaInfo generateGua(GuaNumbers numbers) {
        String upperGua = BAGUA_MAP.get(numbers.upperNum);
        String lowerGua = BAGUA_MAP.get(numbers.lowerNum);
        String originalGua = GUAXIANG_NAME.getOrDefault(upperGua + lowerGua, upperGua + lowerGua);
        
        // 计算变卦和互卦
        String changedGua = calculateChangedGua(upperGua, lowerGua, numbers.movingYao);
        String mutualGua = calculateMutualGua(upperGua, lowerGua);
        
        // 确定体用
        String bodyGua = numbers.movingYao <= 3 ? lowerGua : upperGua;
        String useGua = numbers.movingYao <= 3 ? upperGua : lowerGua;
        
        return new GuaInfo(originalGua, changedGua, mutualGua, bodyGua, useGua);
    }

    /**
     * 分析五行关系
     */
    private static WuxingAnalysis analyzeWuxing(GuaInfo guaInfo) {
        String bodyWuxing = BAGUA_WUXING.get(guaInfo.bodyGua);
        String useWuxing = BAGUA_WUXING.get(guaInfo.useGua);
        String relation = getWuxingRelation(bodyWuxing, useWuxing);
        
        return new WuxingAnalysis(bodyWuxing, useWuxing, relation);
    }

    /**
     * 获取五行关系
     */
    private static String getWuxingRelation(String body, String use) {
        if (body.equals(use)) return "比和";
        if (WUXING_SHENG.get(body).equals(use)) return "体生用";
        if (WUXING_SHENG.get(use).equals(body)) return "用生体";
        if (WUXING_KE.get(body).equals(use)) return "体克用";
        if (WUXING_KE.get(use).equals(body)) return "用克体";
        return "未知";
    }

    /**
     * 计算准确性（传统算法）
     */
    private static double calculateAccuracy(WuxingAnalysis analysis, UserDO user, LunarDate lunarDate) {
        double baseAccuracy = 0.75;
        
        // 根据五行关系调整
        switch (analysis.relation) {
            case "用生体", "比和" -> baseAccuracy += 0.1;
            case "用克体" -> baseAccuracy -= 0.05;
        }
        
        // 用户信息扰动
        if (user != null) {
            if (StrUtil.isNotBlank(user.getCurrentCity())) {
                baseAccuracy += 0.05; // 有地理信息提高准确性
            }
            if (StrUtil.isNotBlank(user.getMoodStatus())) {
                try {
                    int userMood = Integer.parseInt(user.getMoodStatus());
                    if (userMood == 3) baseAccuracy += 0.05; // 平静状态最准确
                } catch (NumberFormatException ignored) {}
            }
        }
        
        return Math.max(0.5, Math.min(0.95, baseAccuracy));
    }

    /**
     * 计算准确性（扰动增强算法）
     */
    private static double calculateAccuracyWithDisturbance(WuxingAnalysis analysis, UserDO user, LunarDate lunarDate, Integer userNumber, Integer mood) {
        double baseAccuracy = 0.75;
        
        // 根据五行关系调整
        switch (analysis.relation) {
            case "用生体", "比和" -> baseAccuracy += 0.1;
            case "用克体" -> baseAccuracy -= 0.05;
        }
        
        // 用户信息扰动
        if (user != null) {
            if (StrUtil.isNotBlank(user.getCurrentCity())) {
                baseAccuracy += 0.05; // 有地理信息提高准确性
            }
            if (StrUtil.isNotBlank(user.getMoodStatus())) {
                try {
                    int userMood = Integer.parseInt(user.getMoodStatus());
                    if (userMood == 3) baseAccuracy += 0.05; // 平静状态最准确
                } catch (NumberFormatException ignored) {}
            }
        }
        
        // 用户输入数字扰动（数字越大，准确性可能越高）
        if (userNumber != null) {
            baseAccuracy += Math.min(0.1, userNumber * 0.01); // 最多增加0.1的准确性
        }
        
        // 心情扰动（心情接近平静时准确性更高）
        if (mood != null) {
            double moodAdjustment = 0.1 - (Math.abs(mood - 3) * 0.02); // 心情偏离3的程度影响准确性
            baseAccuracy += Math.max(0, moodAdjustment); // 最少不减少准确性
        }
        
        return Math.max(0.5, Math.min(0.95, baseAccuracy));
    }

    /**
     * 计算变卦
     */
    private static String calculateChangedGua(String upperGua, String lowerGua, int movingYao) {
        int[] upperYaos = guaToYaos(upperGua);
        int[] lowerYaos = guaToYaos(lowerGua);
        
        int[] allYaos = new int[6];
        System.arraycopy(lowerYaos, 0, allYaos, 0, 3);
        System.arraycopy(upperYaos, 0, allYaos, 3, 3);
        
        // 变爻
        allYaos[movingYao - 1] = 1 - allYaos[movingYao - 1];
        
        String newUpper = yaosToGua(new int[]{allYaos[3], allYaos[4], allYaos[5]});
        String newLower = yaosToGua(new int[]{allYaos[0], allYaos[1], allYaos[2]});
        
        return GUAXIANG_NAME.getOrDefault(newUpper + newLower, newUpper + newLower);
    }

    /**
     * 计算互卦
     */
    private static String calculateMutualGua(String upperGua, String lowerGua) {
        int[] upperYaos = guaToYaos(upperGua);
        int[] lowerYaos = guaToYaos(lowerGua);
        
        int[] allYaos = new int[6];
        System.arraycopy(lowerYaos, 0, allYaos, 0, 3);
        System.arraycopy(upperYaos, 0, allYaos, 3, 3);
        
        String mutualUpper = yaosToGua(new int[]{allYaos[2], allYaos[3], allYaos[4]});
        String mutualLower = yaosToGua(new int[]{allYaos[1], allYaos[2], allYaos[3]});
        
        return GUAXIANG_NAME.getOrDefault(mutualUpper + mutualLower, mutualUpper + mutualLower);
    }

    /**
     * 八卦转爻位
     */
    private static int[] guaToYaos(String gua) {
        return switch (gua) {
            case "乾" -> new int[]{1, 1, 1};
            case "兑" -> new int[]{1, 1, 0};
            case "离" -> new int[]{1, 0, 1};
            case "震" -> new int[]{1, 0, 0};
            case "巽" -> new int[]{0, 1, 1};
            case "坎" -> new int[]{0, 1, 0};
            case "艮" -> new int[]{0, 0, 1};
            case "坤" -> new int[]{0, 0, 0};
            default -> new int[]{0, 0, 0};
        };
    }

    /**
     * 爻位转八卦
     */
    private static String yaosToGua(int[] yaos) {
        String binary = "" + yaos[0] + yaos[1] + yaos[2];
        return switch (binary) {
            case "111" -> "乾";
            case "110" -> "兑";
            case "101" -> "离";
            case "100" -> "震";
            case "011" -> "巽";
            case "010" -> "坎";
            case "001" -> "艮";
            case "000" -> "坤";
            default -> "坤";
        };
    }

    // 内部数据类
    private record GuaNumbers(int upperNum, int lowerNum, int movingYao) {}
    
    private record GuaInfo(String originalGua, String changedGua, String mutualGua,
                          String bodyGua, String useGua) {}
    
    private record WuxingAnalysis(String bodyWuxing, String useWuxing, String relation) {}

}